package com.example.demo.bizhan.distributelock.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.CountDownLatch;

import static java.lang.System.out;

/**
 * @description: xxx
 * @time: 2020/11/3 18:03
 * @author: baojinlong
 **/
@Configuration
@Slf4j
public class DistributedLock {
    private CuratorFramework curatorFramework;
    private static final String ZK_LOCK = "pk-zk-locks";
    private static final String DISTRIBUTE_LOCK = "pk-distributed-locks";
    private static CountDownLatch countDownLatch = new CountDownLatch(1);


    public DistributedLock() {
        curatorFramework = CuratorFrameworkFactory.builder()
                .connectString("192.168.199.151:2181")
                .sessionTimeoutMs(10000)
                .retryPolicy(new ExponentialBackoffRetry(1000, 5))
                .namespace("zk-namespace")
                .build();
        curatorFramework.start();
    }

    @Bean
    public CuratorFramework getClient() {
        curatorFramework = curatorFramework.usingNamespace("zk-namespace");
        // 创建节点  ls /zk-namespace
        try {
            if (curatorFramework.checkExists().forPath("/" + ZK_LOCK) == null) {
                curatorFramework.create()
                        .creatingParentsIfNeeded()
                        .withMode(CreateMode.PERSISTENT)
                        .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE);
            }
            addWatch("/" + ZK_LOCK);
        } catch (Exception e) {
            out.println("zk分布式锁或者客户端失败" + e);
        }
        return curatorFramework;
    }

    /**
     * 监控路径
     *
     * @param path
     */
    private void addWatch(String path) throws Exception {
        PathChildrenCache pathChildrenCache = new PathChildrenCache(curatorFramework, path, true);
        pathChildrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
        pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
            @Override
            public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
                if (pathChildrenCacheEvent.getType().equals(PathChildrenCacheEvent.Type.CHILD_REMOVED)) {
                    String path = pathChildrenCacheEvent.getData().getPath();
                    if (path.contains(DISTRIBUTE_LOCK)) {
                        countDownLatch.countDown();
                    }

                }
            }
        });

    }

    /**
     * 获得分布式锁
     */
    public void getLock() {
        while (true) {
            try {
                curatorFramework.create()
                        .creatingParentsIfNeeded()
                        .withMode(CreateMode.EPHEMERAL)
                        .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
                        .forPath("/" + ZK_LOCK + "/" + DISTRIBUTE_LOCK);
                log.info("分布式锁获得成功...");
            } catch (Exception e) {
                out.println("创建zk节点异常" + e);
                if (countDownLatch.getCount() <= 0) {
                    countDownLatch = new CountDownLatch(1);
                }
                try {
                    countDownLatch.await();
                } catch (InterruptedException interruptedException) {
                    out.println("进程等待异常" + interruptedException);
                }
            }
            return;
        }

    }

    /**
     * 释放分布式锁:订单创建成功或者异常的时候需要释放分布式锁
     *
     * @return
     */
    public boolean releaseLock() {
        String tmpZkPath = "/" + ZK_LOCK + "/" + DISTRIBUTE_LOCK;
        try {
            if (curatorFramework.checkExists().forPath(tmpZkPath) != null) {
                curatorFramework.delete().forPath(tmpZkPath);
            }
        } catch (Exception e) {
            out.println("释放锁发生异常" + e);
            return false;
        }
        log.info("分布式锁释放成功...");
        return true;
    }
}
